#ifndef _BUFFER_IMPL
#define _BUFFER_IMPL

#include "Buffer.h"
#include <new>

using namespace mylib;
using namespace std;

/*implement class Buffer
	std::istream *in_file;				// 0 label read  from input stream
	char *buf;
	int next_pos;
	bool pass_half;					// to label whether pass half of this vector
	bool end;
*/

namespace Buffer_loc {
	const size_t BUF_SIZ = 512;	// the buffer size
	const size_t HAL_BUF_SIZ = 256;
	const char End=0;

	size_t read(istream &in, char *buf, const size_t len);	// return the read number
}

// constructor
Buffer::Buffer(istream &in) : in_file(&in){
	using namespace Buffer_loc;
	try {
		buf = new char[Buffer_loc::BUF_SIZ];
	} catch(...) {
		throw Buffer::Out_of_mem();
	}
	if( read(*in_file, buf, Buffer_loc::HAL_BUF_SIZ) <= 0 ) {
		end = true;
	} else
		end=false;
	pass_half = false;
	next_pos = 0;
}
// operation	
bool Buffer::get(char &c) {
	using namespace Buffer_loc;
	if( buf[next_pos] == End ) {	// the end
		if( next_pos == HAL_BUF_SIZ-1) {
			if ( read(*in_file, buf+HAL_BUF_SIZ, HAL_BUF_SIZ) ) {// read more than one
				next_pos++;	// go to the start of the get block
			} else {
				end=true;
				return false;	// the end of the file
			}
		} else if( next_pos == BUF_SIZ-1 )	{	// the second block
			if( (read(*in_file, buf, HAL_BUF_SIZ)) ) {
				next_pos = 0;
			} else {
				end=true;
				return false;
			}
		} else	{// the end of file
			end=true;
			return false;
		}
	}
	if( next_pos >= Buffer_loc::HAL_BUF_SIZ )
		pass_half = true;
	c = buf[next_pos++];
	return true;
}
// go back n position, the default value for n is 1
size_t Buffer::ret(size_t n){		// go back
	using namespace Buffer_loc;
	if( (pass_half && n >= Buffer_loc::HAL_BUF_SIZ) || (!pass_half &&(size_t) next_pos < n) )
		throw Buffer::Overflow();
	if(!n)
		;
	else {
		end=false;
		if( next_pos < HAL_BUF_SIZ ) {
			next_pos -= n;	//the new position
			if( next_pos < 0 )
				next_pos = BUF_SIZ+next_pos;
		} else {		// the second block
			next_pos -= n;	//the new position
			if( next_pos < HAL_BUF_SIZ ) {	// go to the first block
				next_pos--;
			}
		}
	}
	return n;
}

size_t Buffer::maxret() {					// return maximum return number 
	return (size_t) (pass_half)?Buffer_loc::HAL_BUF_SIZ-1 : next_pos;
}
// implement the base function for Buffer
// return the read number
size_t Buffer_loc::read(istream &in, char *buf, const size_t len) {
	size_t i;
	char c;
	for( i = 0; i < len-1; i++ ) {
		if( in.get(c) )
			buf[i] = c;
		else
			break;
	}
	buf[i] = Buffer_loc::End;
	return i;
}

int Buffer::tellg(){
	int i=next_pos;
	int count=0;
	char c;
	while( (c=buf[i])!=Buffer_loc::End ) {
		count++;
		i++;
		if( c == '\n' )
			count++;
	}
	size_t file_pos;
	if( in_file->eof() ) {
		in_file->seekg(-1, ios::end);
		file_pos = in_file->tellg();
		in_file->seekg(0, ios::end);
	} else {
		file_pos = in_file->tellg();
	}
	return file_pos-count;
}
Buffer &Buffer::seekg(int pos){				// seek get position
	if( pos < 0 )
		throw Buffer::Invalid_input();
	if( in_file->eof() ) 	// at the end
		in_file->seekg(-1, ios::end);
	in_file->seekg(pos);
	if( !Buffer_loc::read(*in_file, buf, Buffer_loc::HAL_BUF_SIZ) )
		end=true;
	next_pos = 0;			// set it the start
	pass_half=false;
	return *this;
}
#endif
